﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Xml;
using System.Xml.Linq;

using AMPLibrary.Collections;

namespace AMPLibrary.Media
{
    [Serializable]
    public class Playlist : ISerializable
    {
        public string Path
        {
            get;
            set;
        }

        public string Title
        {
            get;
            set;
        }

        public Playlist()
        {
        }

        /// <summary>
        /// Method, saves the playlist in m3u format to the specified path
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static bool SavePlaylist(string path, Collection<MediaFile> mediaCollection)
        {
            bool isOk = true;
            string mimeType = MediaTypes.GetMimeType(path);

            switch (mimeType)
            {
                case MimeTypes.M3U:
                    isOk = SaveM3U(path, mediaCollection);
                    break;

                case MimeTypes.WPL:
                    isOk = SaveWPL(path, mediaCollection);
                    break;

                default:
                    isOk = SaveM3U(path, mediaCollection);
                    break;
            }

            if (isOk)
            {
                PlaylistCollection playlistCollection = PlaylistCollection.GetPlaylistCollection();
                playlistCollection.AddItem(new Playlist() { Path = path, Title = System.IO.Path.GetFileNameWithoutExtension(path) });
            }

            return isOk;
        }

        /// <summary>
        /// Method, updates the playlist in m3u format to the specified path
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static bool UpdatePlaylist(string path, Collection<MediaFile> mediaCollection)
        {
            bool isOk = true;
            string mimeType = MediaTypes.GetMimeType(path);

            switch (mimeType)
            {
                case MimeTypes.M3U:
                    isOk = SaveM3U(path, mediaCollection);
                    break;

                case MimeTypes.WPL:
                    isOk = SaveWPL(path, mediaCollection);
                    break;

                default:
                    isOk = SaveM3U(path, mediaCollection);
                    break;
            }

            return isOk;
        }

        /// <summary>
        /// Methods, deletes a playlist from the computer
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static bool DeletePlaylist(string path)
        {
            if (File.Exists(path))
            {
                try
                {
                    File.Delete(path);
                    PlaylistCollection playlistCollection = PlaylistCollection.GetPlaylistCollection();

                    Playlist playlist = (from p in playlistCollection where p.Path == path select p).Single();
                    playlistCollection.RemoveItem(playlist);
                }
                catch
                {
                    return false;
                }
            }
            else
                return false;

            return true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="mediaCollection"></param>
        private static bool SaveWPL(string path, Collection<MediaFile> mediaCollection)
        {
            // set the return value to ok
            bool isOk = true;

            // try and write the file to the path
            try
            {
                FileStream outFileStream = File.Create(path);
                StreamWriter streamWriter = new StreamWriter(outFileStream);

                // assuming everything is ok
                if (outFileStream == null || streamWriter == null)
                    isOk = false;
                else
                {
                    // the first line in an m3u playlist is 
                    streamWriter.WriteLine("<?wpl version=\"1.0\"?>");
                    streamWriter.WriteLine("<smil>");
                    streamWriter.WriteLine("<head>");
                    streamWriter.WriteLine("<title>" + System.IO.Path.GetFileNameWithoutExtension(path) + "<title>");
                    streamWriter.WriteLine("</head>");
                    streamWriter.WriteLine("<body>");
                    streamWriter.WriteLine("<seq>");

                    // foreach media object in this playlist
                    foreach (MediaFile media in mediaCollection)
                        streamWriter.WriteLine("<media src=\"" + media.Path + "\"/>");

                    streamWriter.WriteLine("</seq>");
                    streamWriter.WriteLine("</body>");
                    streamWriter.WriteLine("</smil>");
                }

                // close the streams
                streamWriter.Close();
                outFileStream.Close();
            }
            catch
            {
                // if an error occured change the return value
                isOk = false;
            }

            return isOk;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="mediaCollection"></param>
        private static bool SaveM3U(string path, Collection<MediaFile> mediaCollection)
        {
            // set the return value to ok
            bool isOk = true;

            // try and write the file to the path
            try
            {
                if (File.Exists(path))
                    File.Delete(path);

                FileStream outFileStream = File.Create(path);
                StreamWriter streamWriter = new StreamWriter(outFileStream);

                // assuming everything is ok
                if (outFileStream == null || streamWriter == null)
                    isOk = false;
                else
                {
                    // the first line in an m3u playlist is 
                    streamWriter.WriteLine("#EXTM3U");
                    streamWriter.WriteLine(string.Empty);

                    // foreach media object in this playlist
                    foreach (MediaFile media in mediaCollection)
                    {
                        streamWriter.WriteLine("#EXTINF:" + media.Duration.ToString() + ", " + media.Tag.Title);
                        streamWriter.WriteLine(media.Path);
                        streamWriter.WriteLine(string.Empty);
                    }
                }

                // close the streams
                streamWriter.Close();
                outFileStream.Close();
            }
            catch
            {
                // if an error occured change the return value
                isOk = false;
            }

            return isOk;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static Collection<MediaFile> LoadPlaylist(string path)
        {
            Collection<MediaFile> loadedMedia = new Collection<MediaFile>();

            if (!MediaTypes.IsValidPlaylistMediaType(path))
                return loadedMedia;

            string mimeType = MediaTypes.GetMimeType(path);

            switch (mimeType)
            {
                case MimeTypes.M3U:
                case MimeTypes.M3Ux:
                    loadedMedia = LoadM3U(path);
                    break;
                case MimeTypes.WPL:
                    loadedMedia = LoadWPL(path);
                    break;
            }

            return loadedMedia;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static Collection<MediaFile> LoadM3U(string path)
        {
            Collection<MediaFile> mediaCollection = new Collection<MediaFile>();

            FileStream inFileStream = null;
            StreamReader streamReader = null;

            try
            {
                inFileStream = File.Open(path, FileMode.Open);
                streamReader = new StreamReader(inFileStream);

                string line = streamReader.ReadLine();
                if (line.Equals("#EXTM3U"))
                {
                    line = streamReader.ReadLine();
                    while (line != null)
                    {
                        if (line.StartsWith("#EXTINF:"))
                        {
                            line = line.Replace("#EXTINF:", "");
                            line = streamReader.ReadLine();

                            if (File.Exists(line))
                                mediaCollection.AddItem(MediaFile.LoadMedia(line));

                            line = streamReader.ReadLine();
                        }

                        line = streamReader.ReadLine();
                    }
                }
                else
                {
                }
            }
            catch
            {
            }
            finally
            {
                if (streamReader != null)
                    streamReader.Close();

                if (inFileStream != null)
                    inFileStream.Close();
            }

            return mediaCollection;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private static Collection<MediaFile> LoadWPL(string path)
        {
            Collection<MediaFile> mediaCollection = new Collection<MediaFile>();
            XmlTextReader playlistFileReader = null;

            try
            {
                playlistFileReader = new XmlTextReader(path);

                while (playlistFileReader.Read())
                {
                    if (playlistFileReader.NodeType == XmlNodeType.Element)
                    {
                        if (playlistFileReader.LocalName.Equals("media"))
                        {
                            if(File.Exists(playlistFileReader.GetAttribute(0).ToString()))
                                mediaCollection.AddItem(MediaFile.LoadMedia(playlistFileReader.GetAttribute(0).ToString()));
                        }
                    }
                }                
            }
            catch
            {
            }
            finally
            {
                if (playlistFileReader != null)
                    playlistFileReader.Close();
            }

            return mediaCollection;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="media"></param>
        public static void AddItem(string path, MediaFile media)
        {
            Collection<MediaFile> currentPlaylistCollection = Playlist.LoadPlaylist(path);

            currentPlaylistCollection.AddItem(media);

            Playlist.UpdatePlaylist(path, currentPlaylistCollection);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="path"></param>
        /// <param name="media"></param>
        public static void AddItems(string path, Collection<MediaFile> media)
        {
            Collection<MediaFile> currentPlaylistCollection = Playlist.LoadPlaylist(path);

            foreach(MediaFile item in media)
                currentPlaylistCollection.AddItem(item);

            Playlist.UpdatePlaylist(path, currentPlaylistCollection);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("Path", this.Path);
            info.AddValue("Title", this.Title);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public Playlist(SerializationInfo info, StreamingContext ctxt)
        {
            this.Path = (string)info.GetValue("Path", typeof(string));
            this.Title = (string)info.GetValue("Title", typeof(string));
        }
    }
}
